﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
using eCommerce.Core.Data;
using eCommerce.Core.Domain.Pictures;

namespace eCommerce.Services.Pictures
{
    public class PictureService:IPictureService
    {
        private static readonly object s_lock = new object();

        private IRepository<Picture> _pictureRepository;

        public PictureService(IRepository<Picture> pictureRepository)
        {
            _pictureRepository = pictureRepository;
        }



        public string GetPictureUrl(int pictureId,int targetSize=0,bool showDefaultPicture=true)
        {
            var picture = GetPictureById(pictureId);
            return GetPictureUrl(picture, targetSize, showDefaultPicture);
        }

        public Picture Resize(int pictureId, int targetSize)
        {
            if (pictureId == 0)
                return null;

            var picture = _pictureRepository.GetById(pictureId);

            var pictureBinary = LoadPictureBinary(picture);
          

            Picture resizedPic = null;

            lock (s_lock)
            {
                if (targetSize == 0)
                {
                    return picture;
                }
                else
                {

                    using (var stream = new MemoryStream(pictureBinary))
                    {
                        Bitmap b = null;
                        try
                        {
                            //try-catch to ensure that picture binary is really OK. Otherwise, we can get "Parameter is not valid" exception if binary is corrupted for some reasons
                            b = new Bitmap(stream);

                        }
                        catch (ArgumentException exc)
                        {
                            //_logger.Error(string.Format("Error generating picture thumb. ID={0}", picture.Id), exc);
                        }
                        if (b == null)
                        {
                            //bitmap could not be loaded for some reasons
                            return null;
                        }


                        var newSize = CalculateDimensions(b.Size, targetSize);

                        if (newSize.Width < 1)
                            newSize.Width = 1;
                        if (newSize.Height < 1)
                            newSize.Height = 1;

                        using (var newBitMap = new Bitmap(newSize.Width, newSize.Height))
                        {
                            using (var g = Graphics.FromImage(newBitMap))
                            {
                                g.SmoothingMode = SmoothingMode.HighQuality;
                                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                                g.CompositingQuality = CompositingQuality.HighQuality;
                                g.PixelOffsetMode = PixelOffsetMode.HighQuality;
                                g.DrawImage(b, 0, 0, newSize.Width, newSize.Height);

                            }


                            var converter = new ImageConverter();
                            var picBytes = (byte[]) converter.ConvertTo(newBitMap, typeof (byte[]));

                            resizedPic = InsertPicture(picBytes, picture.MimeType, null, true);

                        }
                        b.Dispose();
                    }
                }
            }
            return resizedPic;
        }

        public Picture GetPictureById(int pictureId)
        {
            if (pictureId == 0)
                return null;

            var picture = _pictureRepository.GetById(pictureId);
            return picture;
        }


        public virtual string GetPictureUrl(Picture picture, int targetSize = 0, bool showDefaultPicture = true)
        {
            string url = string.Empty;
            byte[] pictureBinary = null;
            if (picture != null)
                pictureBinary = LoadPictureBinary(picture);
            if (picture == null || pictureBinary == null || pictureBinary.Length == 0)
            {
                if (showDefaultPicture)
                {
                    url = GetDefaultPictureUrl(targetSize);
                }
                return url;
            }

            string lastPart = GetFileExtensionFromMimeType(picture.MimeType);
            string localFilename;



            lock (s_lock)
            {
                if (targetSize == 0)
                {
                    localFilename = 
                        string.Format("{0}.{1}", picture.Id.ToString("0000000"), lastPart);

                    if (!File.Exists(Path.Combine(this.LocalThumbImagePath, localFilename)))
                    {
                        if (!System.IO.Directory.Exists(this.LocalThumbImagePath))
                        {
                            System.IO.Directory.CreateDirectory(this.LocalThumbImagePath);
                        }
                        File.WriteAllBytes(Path.Combine(this.LocalThumbImagePath, localFilename), pictureBinary);
                    }
                }
                else
                {
                    localFilename = string.Format("{0}_{1}.{2}", picture.Id.ToString("0000000"), targetSize, lastPart);
                    if (!File.Exists(Path.Combine(this.LocalThumbImagePath, localFilename)))
                    {
                        if (!System.IO.Directory.Exists(this.LocalThumbImagePath))
                        {
                            System.IO.Directory.CreateDirectory(this.LocalThumbImagePath);
                        }
                        using (var stream = new MemoryStream(pictureBinary))
                        {
                            Bitmap b = null;
                            try
                            {
                                //try-catch to ensure that picture binary is really OK. Otherwise, we can get "Parameter is not valid" exception if binary is corrupted for some reasons
                                b = new Bitmap(stream);
                               

                            }
                            catch (ArgumentException exc)
                            {
                                //_logger.Error(string.Format("Error generating picture thumb. ID={0}", picture.Id), exc);
                            }
                            if (b == null)
                            {
                                //bitmap could not be loaded for some reasons
                                return url;
                            }
                            var newSize = CalculateDimensions(b.Size, targetSize);

                            if (newSize.Width < 1)
                                newSize.Width = 1;
                            if (newSize.Height < 1)
                                newSize.Height = 1;

                            using (var newBitMap = new Bitmap(newSize.Width, newSize.Height))
                            {
                                using (var g = Graphics.FromImage(newBitMap))
                                {
                                    g.SmoothingMode = SmoothingMode.HighQuality;
                                    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                                    g.CompositingQuality = CompositingQuality.HighQuality;
                                    g.PixelOffsetMode = PixelOffsetMode.HighQuality;
                                    g.DrawImage(b, 0, 0, newSize.Width, newSize.Height);
                                    var ep = new EncoderParameters();
                                    ep.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
                                    ImageCodecInfo ici = GetImageCodecInfoFromExtension(lastPart);
                                    if (ici == null)
                                        ici = GetImageCodecInfoFromMimeType("image/jpeg");
                                    newBitMap.Save(Path.Combine(this.LocalThumbImagePath, localFilename), ici, ep);
                                    //newBitMap.Save(Path.Combine(this.LocalThumbImagePath, localFilename));
                                }
                            }
                            b.Dispose();
                        }
                    }
                }
            }
            url =  GetHost()  + "/content/images/thumbs/" + localFilename;
            return url;
        }

        private string GetHost()
        {
            return ConfigurationManager.AppSettings["HostName"];
        }


        public IList<Picture> GetPicturesByProductId(int id)
        {
            throw new NotImplementedException();
        }

        public virtual byte[] CropImage(int pictureId, int x, int y, int w, int h)
        {

            if (pictureId == 0)
                return null;

            var picture = _pictureRepository.GetById(pictureId);

            var pictureBinary =  LoadPictureBinary(picture);

            var lastPart = GetFileExtensionFromMimeType(picture.MimeType);

            //var localFilename =
            //           string.Format("{0}_{1}_{2}.{3}", picture.Id.ToString("0000000"), w,h, lastPart);

            var cropRect = new Rectangle(x, y, w, h);
            var target = new Bitmap(cropRect.Width, cropRect.Height);

            using (var stream = new MemoryStream(pictureBinary))
            {
                try
                {
                    //try-catch to ensure that picture binary is really OK. Otherwise, we can get "Parameter is not valid" exception if binary is corrupted for some reasons
                    var src = new Bitmap(stream);

                    using (Graphics g = Graphics.FromImage(target))
                    {
                        g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
                                    cropRect,
                                    GraphicsUnit.Pixel);



                        //using (MemoryStream ms = new MemoryStream())
                        //{
                        //    target.Save(ms, format);
                        //    return ms.ToArray();
                        //}

                        ImageConverter converter = new ImageConverter();
                        return (byte[])converter.ConvertTo(target, typeof(byte[]));

                        
                        //var ep = new EncoderParameters();
                        //ep.Param[0] = new EncoderParameter(Encoder.Quality, 100);
                        //ImageCodecInfo ici = GetImageCodecInfoFromExtension(lastPart);
                        //if (ici == null)
                        //    ici = GetImageCodecInfoFromMimeType("image/jpeg");

                        //target.Save(Path.Combine(this.LocalThumbImagePath, localFilename), ici, ep);
                                   
                    }
                }
                catch (Exception e)
                {

                }
            }
            return null;
            //var url = "/content/images/thumbs/" + localFilename;
            //return url;

        }

        public virtual string GetDefaultPictureUrl(int targetSize = 0)
        {
            string defaultImageName = "nophoto.jpg";
            


            string relPath =  "/content/images/" + defaultImageName;
            if (targetSize == 0)
                return relPath;
            else
            {
                var fileExtension = Path.GetExtension(defaultImageName);
                string fname = string.Format("{0}_{1}{2}",
                       Path.GetFileNameWithoutExtension(defaultImageName),
                       targetSize,
                       fileExtension);

                var localPath = HttpContext.Current.Server.MapPath("~/content/images");

                string filePath = Path.Combine(localPath, defaultImageName);
                var path = Path.Combine(LocalThumbImagePath, fname);
                   
                    if (!File.Exists(path))
                    {
                        using (var b = new Bitmap(filePath))
                        {
                            var newSize = CalculateDimensions(b.Size, targetSize);

                            if (newSize.Width < 1)
                                newSize.Width = 1;
                            if (newSize.Height < 1)
                                newSize.Height = 1;

                            using (var newBitMap = new Bitmap(newSize.Width, newSize.Height))
                            {
                                using (var g = Graphics.FromImage(newBitMap))
                                {
                                    g.SmoothingMode = SmoothingMode.HighQuality;
                                    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                                    g.CompositingQuality = CompositingQuality.HighQuality;
                                    g.PixelOffsetMode = PixelOffsetMode.HighQuality;
                                    g.DrawImage(b, 0, 0, newSize.Width, newSize.Height);
                                    //var ep = new EncoderParameters();
                                    //ep.Param[0] = new EncoderParameter(Encoder.Quality, 100);
                                    //ImageCodecInfo ici = GetImageCodecInfoFromExtension(fileExtension);
                                    //if (ici == null)
                                    //    ici = GetImageCodecInfoFromMimeType("image/jpeg");
                                    newBitMap.Save(path);
                                }
                            }
                        }
                    }
                    return  "/content/images/thumbs/" + fname;
              
            }
        }

        protected virtual string GetFileExtensionFromMimeType(string mimeType)
        {
            if (mimeType == null)
                return null;

            string[] parts = mimeType.Split('/');
            string lastPart = parts[parts.Length - 1];
            switch (lastPart)
            {
                case "pjpeg":
                    lastPart = "jpg";
                    break;
                case "x-png":
                    lastPart = "png";
                    break;
                case "x-icon":
                    lastPart = "ico";
                    break;
            }
            return lastPart;
        }

        protected virtual ImageCodecInfo GetImageCodecInfoFromExtension(string fileExt)
        {
            fileExt = fileExt.TrimStart(".".ToCharArray()).ToLower().Trim();
            switch (fileExt)
            {
                case "jpg":
                case "jpeg":
                    return GetImageCodecInfoFromMimeType("image/jpeg");
                case "png":
                    return GetImageCodecInfoFromMimeType("image/png");
                case "gif":
                    //use png codec for gif to preserve transparency
                    //return GetImageCodecInfoFromMimeType("image/gif");
                    return GetImageCodecInfoFromMimeType("image/png");
                default:
                    return GetImageCodecInfoFromMimeType("image/jpeg");
            }
        }


        protected virtual ImageCodecInfo GetImageCodecInfoFromMimeType(string mimeType)
        {
            var info = ImageCodecInfo.GetImageEncoders();
            foreach (var ici in info)
                if (ici.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))
                    return ici;
            return null;
        }

        protected virtual Size CalculateDimensions(Size originalSize, int targetSize)
        {
            var newSize = new Size();
            if (originalSize.Height > originalSize.Width) // portrait 
            {
                newSize.Width = (int)(originalSize.Width * (float)(targetSize / (float)originalSize.Height));
                newSize.Height = targetSize;
            }
            else // landscape or square
            {
                newSize.Height = (int)(originalSize.Height * (float)(targetSize / (float)originalSize.Width));
                newSize.Width = targetSize;
            }
            return newSize;
        }
        protected virtual string LocalThumbImagePath
        {
            get
            {
                string path = HttpContext.Current.Server.MapPath("~/content/images/thumbs");
                return path;
            }
        }

        private byte[] LoadPictureBinary(Picture picture)
        {
            return picture.PictureBinary;
        }


        public Picture InsertPicture(byte[] fileBinary, string contentType, object o, bool b)
        {
            var picture = new Picture()
            {
                PictureBinary = fileBinary,
                MimeType = contentType,
            };
            _pictureRepository.Insert(picture);
            return picture;
        }
    }
}